﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Collections.ObjectModel;
using System.Xml.Serialization;
using System.Xml;
using System.Xml.Schema;
using System.Reflection;
using System.IO;
using System.Runtime.Serialization;
using Barbar.Colonization.Core;

namespace Barbar.Colonization.Model.RuleSets {
  [XmlType(Namespace=Constants.XmlRootNamespace, TypeName="RuleSet")]
  public class XmlRuleSet : IRuleSet, IDeserializationCallback {
    private Collection<XmlImage> m_XmlImages = new Collection<XmlImage>();
    private Collection<XmlUnitType> m_XmlUnitTypes = new Collection<XmlUnitType>();
    private Collection<XmlTerrainType> m_XmlTerrainTypes = new Collection<XmlTerrainType>();
    private XmlTileTypes m_XmlTileTypes = new XmlTileTypes();
    private Collection<XmlRawMaterialType> m_XmlRawMaterialTypes = new Collection<XmlRawMaterialType>();
    private Collection<XmlBonusResourceType> m_XmlBonusResouceTypes = new Collection<XmlBonusResourceType>();

    private ReadOnlyCollection<IImage> m_Images;
    private ReadOnlyCollection<IUnitType> m_UnitTypes;
    private ReadOnlyCollection<ITerrainType> m_TerrainTypes;
    private TileTypeCollection m_TileTypeCollection;
    private ReadOnlyCollection<IRawMaterialType> m_RawMaterialTypes;
    private ReadOnlyCollection<IBonusResourceType> m_BonusResourceTypes;

    public static XmlRuleSet LoadRuleSet(string fileName) {
      Assembly assembly = Assembly.GetExecutingAssembly();
      XmlSchema schema;
      using(Stream stream = assembly.GetManifestResourceStream("Barbar.Colonization.Model.RuleSets.XmlRuleSet.xsd")) {
        schema = XmlSchema.Read(stream, delegate(object sender, ValidationEventArgs e) {
          if (e.Severity == XmlSeverityType.Error)
            throw e.Exception;
        });
      }
      XmlReaderSettings settings = new XmlReaderSettings();
      settings.ValidationType = ValidationType.Schema;
      settings.Schemas.Add(schema);

      XmlSerializer serializer = new XmlSerializer(typeof(XmlRuleSet), Constants.XmlRootNamespace);
      XmlRuleSet ruleSet;

      using (FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read)) {
        using (XmlReader reader = XmlReader.Create(stream, settings)) {
          ruleSet = (XmlRuleSet)serializer.Deserialize(reader);
        }
      }
      ((IDeserializationCallback)ruleSet).OnDeserialization(null);

      return ruleSet;
    }

    public Collection<XmlImage> Images {
      get { return m_XmlImages; }
    }

    public Collection<XmlTerrainType> TerrainTypes {
      get { return m_XmlTerrainTypes; }
    }

    public XmlTileTypes TileTypes {
      get { return m_XmlTileTypes; }
      set { m_XmlTileTypes = value; }
    }

    public Collection<XmlRawMaterialType> RawMaterialTypes {
      get { return m_XmlRawMaterialTypes; }
    }

    public Collection<XmlBonusResourceType> BonusResourceTypes {
      get { return m_XmlBonusResouceTypes; }
    }

    public Collection<XmlUnitType> UnitTypes {
      get { return m_XmlUnitTypes; }
    }

    #region IRuleSet Members

    ReadOnlyCollection<IImage> IRuleSet.Images {
      get { return m_Images; }
    }

    ReadOnlyCollection<IUnitType> IRuleSet.UnitTypes {
      get { return m_UnitTypes; }
    }

    ReadOnlyCollection<CityType> IRuleSet.CityTypes {
      get { throw new NotImplementedException(); }
    }

    TileTypeCollection IRuleSet.TileTypes {
      get { return m_TileTypeCollection; }
    }

    ReadOnlyCollection<RaceType> IRuleSet.RaceTypes {
      get { throw new NotImplementedException(); }
    }

    ReadOnlyCollection<BuildingType> IRuleSet.BuildingTypes {
      get { throw new NotImplementedException(); }
    }

    ReadOnlyCollection<IRawMaterialType> IRuleSet.RawMaterialTypes {
      get { return m_RawMaterialTypes; }
    }

    #endregion

    #region IDeserializationCallback Members

    void IDeserializationCallback.OnDeserialization(object sender) {
      foreach (XmlUnitType unitType in m_XmlUnitTypes)
        ((IRulesetDeserialization)unitType).OnDeserialization(this);
      foreach (XmlTerrainType terrainType in m_XmlTerrainTypes)
        ((IRulesetDeserialization)terrainType).OnDeserialization(this);
      foreach (XmlTileType tileType in m_XmlTileTypes.Types)
        ((IRulesetDeserialization)tileType).OnDeserialization(this);
      foreach (XmlRawMaterialType rawMaterialType in m_XmlRawMaterialTypes)
        ((IRulesetDeserialization)rawMaterialType).OnDeserialization(this);
      foreach (XmlImage image in m_XmlImages)
        ((IRulesetDeserialization)image).OnDeserialization(this);

      m_Images = new ReadOnlyCollection<IImage>(CollectionConvert.ToCollection<IImage, XmlImage>(m_XmlImages));
      m_UnitTypes = new ReadOnlyCollection<IUnitType>(CollectionConvert.ToCollection<IUnitType, XmlUnitType>(m_XmlUnitTypes));
      m_TerrainTypes = new ReadOnlyCollection<ITerrainType>(CollectionConvert.ToCollection<ITerrainType, XmlTerrainType>(m_XmlTerrainTypes));
      m_TileTypeCollection = new TileTypeCollection(CollectionConvert.ToCollection<ITileType, XmlTileType>(m_XmlTileTypes.Types), m_XmlTileTypes.TileType);
      m_RawMaterialTypes = new ReadOnlyCollection<IRawMaterialType>(CollectionConvert.ToCollection<IRawMaterialType, XmlRawMaterialType>(m_XmlRawMaterialTypes));
      m_BonusResourceTypes = new ReadOnlyCollection<IBonusResourceType>(CollectionConvert.ToCollection<IBonusResourceType, XmlBonusResourceType>(m_XmlBonusResouceTypes));
    }

    #endregion
  }
}
